From: Colin Walters Date: Fri, 4 Sep 2009 01:05:16 +0000 (-0400) Subject: Add ShellMenu X-Git-Tag: archive/raspbian/50.5-1+rpi1~1^2~1013^2^2~10956 X-Git-Url: https://dgit.raspbian.org/%22http://www.example.com/cgi/%22/%22http:/www.example.com/cgi/%22?a=commitdiff_plain;h=22c445cffc14ebafdd46cba83d86a6e26c0e13ee;p=gnome-shell.git Add ShellMenu An object with methods and signals useful for implementing popup menus. --- diff --git a/src/Makefile.am b/src/Makefile.am index 9dd9ff8eb..da7de3812 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -75,6 +75,8 @@ libgnome_shell_la_SOURCES = \ shell-generic-container.h \ shell-gtk-embed.c \ shell-gtk-embed.h \ + shell-menu.c \ + shell-menu.h \ shell-overflow-list.c \ shell-overflow-list.h \ shell-process.c \ diff --git a/src/shell-menu.c b/src/shell-menu.c new file mode 100644 index 000000000..6debaf8f7 --- /dev/null +++ b/src/shell-menu.c @@ -0,0 +1,243 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ + +/** + * SECTION:shell-menu + * @short_description: A box which acts like a popup menu + * + * A #BigBox subclass which adds methods and signals useful for implementing + * popup-menu like actors. + */ + +#include "shell-menu.h" + +G_DEFINE_TYPE(ShellMenu, shell_menu, BIG_TYPE_BOX); + +struct _ShellMenuPrivate { + gboolean have_grab; + + ClutterActor *selected; +}; + +/* Signals */ +enum +{ + UNSELECTED, + SELECTED, + ACTIVATE, + POPDOWN, + LAST_SIGNAL +}; + +static guint shell_menu_signals [LAST_SIGNAL] = { 0 }; + +static gboolean +shell_menu_contains (ShellMenu *box, + ClutterActor *actor) +{ + while (actor != NULL && actor != (ClutterActor*)box) + { + actor = clutter_actor_get_parent (actor); + } + return actor != NULL; +} + +static void +on_selected_destroy (ClutterActor *actor, + ShellMenu *box) +{ + box->priv->selected = NULL; +} + +static void +set_selected (ShellMenu *box, + ClutterActor *actor) +{ + if (actor == box->priv->selected) + return; + if (box->priv->selected) + { + g_signal_handlers_disconnect_by_func (box->priv->selected, G_CALLBACK(on_selected_destroy), box); + g_signal_emit (G_OBJECT (box), shell_menu_signals[UNSELECTED], 0, box->priv->selected); + } + box->priv->selected = actor; + if (box->priv->selected) + { + g_signal_connect (box->priv->selected, "destroy", G_CALLBACK(on_selected_destroy), box); + g_signal_emit (G_OBJECT (box), shell_menu_signals[SELECTED], 0, box->priv->selected); + } +} + +static gboolean +shell_menu_enter_event (ClutterActor *actor, + ClutterCrossingEvent *event) +{ + ShellMenu *box = SHELL_MENU (actor); + + if (!shell_menu_contains (box, event->source)) + return TRUE; + + if (event->source == (ClutterActor*)box) + return TRUE; + + if (g_object_get_data (G_OBJECT (event->source), "shell-is-separator")) + return TRUE; + + set_selected (box, event->source); + + return TRUE; +} + +static gboolean +shell_menu_leave_event (ClutterActor *actor, + ClutterCrossingEvent *event) +{ + ShellMenu *box = SHELL_MENU (actor); + + set_selected (box, NULL); + + return TRUE; +} + +static gboolean +shell_menu_button_release_event (ClutterActor *actor, + ClutterButtonEvent *event) +{ + ShellMenu *box = SHELL_MENU (actor); + + if (event->button != 1) + return FALSE; + + shell_menu_popdown (box); + + if (!shell_menu_contains (box, event->source)) + return FALSE; + + if (box->priv->selected == NULL) + return FALSE; + + g_signal_emit (G_OBJECT (box), shell_menu_signals[ACTIVATE], 0, box->priv->selected); + + return TRUE; +} + +void +shell_menu_popup (ShellMenu *box, + guint button, + guint32 activate_time) +{ + box->priv->have_grab = TRUE; + clutter_grab_pointer (CLUTTER_ACTOR (box)); +} + +void +shell_menu_popdown (ShellMenu *box) +{ + if (box->priv->have_grab) + clutter_ungrab_pointer (); + clutter_actor_hide (CLUTTER_ACTOR (box)); + g_signal_emit (G_OBJECT (box), shell_menu_signals[POPDOWN], 0); +} + +/** + * shell_menu_append_separator: + * @box: + * @separator: An actor which functions as a menu separator + * @flags: Packing flags + * + * Actors added to the menu with default functions are treated like + * menu items; this function will add an actor that should instead + * be treated like a menu separator. The current practical effect + * is that the separators will not be selectable. + */ +void +shell_menu_append_separator (ShellMenu *box, + ClutterActor *separator, + BigBoxPackFlags flags) +{ + g_object_set_data (G_OBJECT (separator), "shell-is-separator", GUINT_TO_POINTER(TRUE)); + big_box_append (BIG_BOX (box), separator, flags); +} + +static void +shell_menu_class_init (ShellMenuClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); + + actor_class->enter_event = shell_menu_enter_event; + actor_class->leave_event = shell_menu_leave_event; + actor_class->button_release_event = shell_menu_button_release_event; + + /** + * ShellMenu::unselected + * @box: The #ShellMenu + * @actor: The previously hovered-over menu item + * + * This signal is emitted when a menu item transitions to + * an unselected state. + */ + shell_menu_signals[UNSELECTED] = + g_signal_new ("unselected", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, CLUTTER_TYPE_ACTOR); + + /** + * ShellMenu::selected + * @box: The #ShellMenu + * @actor: The hovered-over menu item + * + * This signal is emitted when a menu item is in a selected state. + */ + shell_menu_signals[SELECTED] = + g_signal_new ("selected", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, CLUTTER_TYPE_ACTOR); + + /** + * ShellMenu::activate + * @box: The #ShellMenu + * @actor: The clicked menu item + * + * This signal is emitted when a menu item is selected. + */ + shell_menu_signals[ACTIVATE] = + g_signal_new ("activate", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, CLUTTER_TYPE_ACTOR); + + /** + * ShellMenu::popdown + * @box: The #ShellMenu + * + * This signal is emitted when the menu is removed from the display. + */ + shell_menu_signals[POPDOWN] = + g_signal_new ("popdown", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, 0); + + g_type_class_add_private (gobject_class, sizeof (ShellMenuPrivate)); +} + +static void +shell_menu_init (ShellMenu *self) +{ + self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, SHELL_TYPE_MENU, + ShellMenuPrivate); +} diff --git a/src/shell-menu.h b/src/shell-menu.h new file mode 100644 index 000000000..1d0c57111 --- /dev/null +++ b/src/shell-menu.h @@ -0,0 +1,39 @@ +#ifndef __SHELL_MENU_H__ +#define __SHELL_MENU_H__ + +#include +#include "big/box.h" + +#define SHELL_TYPE_MENU (shell_menu_get_type ()) +#define SHELL_MENU(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SHELL_TYPE_MENU, ShellMenu)) +#define SHELL_MENU_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SHELL_TYPE_MENU, ShellMenuClass)) +#define SHELL_IS_MENU(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SHELL_TYPE_MENU)) +#define SHELL_IS_MENU_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SHELL_TYPE_MENU)) +#define SHELL_MENU_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SHELL_TYPE_MENU, ShellMenuClass)) + +typedef struct _ShellMenu ShellMenu; +typedef struct _ShellMenuClass ShellMenuClass; + +typedef struct _ShellMenuPrivate ShellMenuPrivate; + +struct _ShellMenu +{ + BigBox parent; + + ShellMenuPrivate *priv; +}; + +struct _ShellMenuClass +{ + BigBoxClass parent_class; +}; + +GType shell_menu_get_type (void) G_GNUC_CONST; + +void shell_menu_popup (ShellMenu *behavior, guint button, guint32 activate_time); + +void shell_menu_append_separator (ShellMenu *behavior, ClutterActor *separator, BigBoxPackFlags flags); + +void shell_menu_popdown (ShellMenu *behavior); + +#endif /* __SHELL_MENU_H__ */